package org.zn.note.java8;

import org.zn.note.java8.Bean.Apple;

import java.util.ArrayList;
import java.util.List;

/**
 * 行为参数化  --- 是可以处理频繁变更的需求的一种软件开发模式。
 * 类似策略模式 ---定义一些算法簇(行为)，然后在运行时传入算法簇，通过算法簇决定行为
 * <p>
 * 1、筛选绿苹果 {@link #filterGreenApples}
 * <p>
 * 2、如果要筛选红苹果呢？ {@link #filterApplesByColor}
 * 将颜色参数化
 * <p>
 * 3、如果要筛选重量呢？ {@link #filterApplesByWeight}
 * 新加一个方法
 * <p>
 * 4、如果还有别的筛选条件呢？ {@link #filterApples}
 * 将行为参数化，对选择标准建模：对苹果的不同属性，返回boolean  ---谓词（返回boolean的函数）
 * 多个谓词实现，代表不同的选择标准，也可以理解为策略模式：这些选择的标准就是不同的filter行为、就是不同的算法簇，然后在运行的时候选择一个算法
 * 如果要增加新的筛选条件，就加一个新的谓词。这里其实已经将行为参数化了，只不过java方法只能接受对象，所以行为要封装到对象中
 * <p>
 * 5、每个行为都要有一个谓词实现，比较啰嗦，用匿名类优化 {@link #filterApples}
 * <p>
 * 6、也很啰嗦
 * 我们只关注功能本身，为什么还要那么啰嗦的写一些无关的代码？  --- lombok
 * 写代码最好只有功能相关的，看着整洁，也方便，而不是有很多的“模板”代码
 * 这样，代码更像是在描述问题本身
 * <p>
 * 7、如果不是苹果，是香蕉呢？
 * 将List类型抽象化
 */
public class Ch02_FilterApple {

    public static void main(String[] args) {
        List<Apple> inventory = Apple.genApples();
        List<Apple> result = null;
        // 筛选绿苹果
        result = filterGreenApples(inventory);
        System.out.println(result);
        // 如果筛选红苹果呢？  --- 颜色参数化
        result = filterApplesByColor(inventory, "red");
        System.out.println(result);
        // 又要筛选重量了，又得加一个方法
        result = filterApplesByWeight(inventory, 120);
        System.out.println(result);
        // 用谓词代表筛选条件，filter只需要接受谓词，然后调用谓词，真正的筛选在谓词里
        // 而如果要增加新的筛选条件，就加一个新的谓词
        // 这里其实已经将行为参数化了，只不过java方法只能接受对象，所以行为要封装到对象中
        result = filterApples(inventory, new AppleGreenColorPredicate());
        System.out.println(result);
        // 不为每个行为创建谓词实现，用匿名类直接传入行为
        result = filterApples(inventory, new ApplePredicate() {
            @Override
            public boolean test(Apple apple) {
                return "green".equals(apple.getColor());
            }
        });
        System.out.println(result);
        // lombok
        result = filterApples(inventory, (Apple apple) -> "green".equals(apple.getColor()));
        System.out.println(result);
        // 将List类型抽象化
        result = filter(inventory, (Apple apple) -> "green".equals(apple.getColor()));
        System.out.println(result);
    }

    // 筛选绿苹果
    public static List<Apple> filterGreenApples(List<Apple> inventory) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : inventory)
            if ("green".equals(apple.getColor()))
                result.add(apple);
        return result;
    }

    // 如果要筛选红苹果呢？
    // 颜色参数化
    public static List<Apple> filterApplesByColor(List<Apple> inventory, String color) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : inventory)
            if (apple.getColor().equals(color))
                result.add(apple);
        return result;
    }

    // 筛选重量
    public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) {
        List<Apple> result = new ArrayList<Apple>();
        for (Apple apple : inventory)
            if (apple.getWeight() > weight)
                result.add(apple);
        return result;
    }

    /**
     * 行为参数化
     * 对选择标准建模：对苹果的不同属性，返回boolean  ---谓词（返回boolean的函数）
     * 多个谓词实现，代表不同的选择标准
     * 也可以理解为策略模式：这些选择的标准就是不同的filter行为、就是不同的算法簇，然后在运行的时候选择一个算法
     */
    private interface ApplePredicate { // 苹果谓词
        boolean test(Apple apple);
    }

    private static class AppleHeavyWeightPredicate implements ApplePredicate {
        @Override
        public boolean test(Apple apple) {
            return apple.getWeight() > 150;
        }
    }

    private static class AppleGreenColorPredicate implements ApplePredicate {
        public boolean test(Apple apple) {
            return "green".equals(apple.getColor());
        }
    }

    public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : inventory)
            if (p.test(apple))
                result.add(apple);
        return result;
    }

    // 用匿名类优化
    public static List<Apple> filterApples(List<Apple> inventory) {
        return filterApples(inventory, new ApplePredicate() {
            @Override
            public boolean test(Apple apple) {
                return "green".equals(apple.getColor());
            }
        });
    }

    // lombok
    public static List<Apple> filterApplesByLombok(List<Apple> inventory) {
        return filterApples(inventory, apple -> "green".equals(apple.getColor()));
    }

    // 将List类型抽象化
    public interface Predicate<T> {
        boolean test(T t);
    }

    public static <T> List<T> filter(List<T> list, Predicate<T> p) {
        List<T> result = new ArrayList<>();
        for (T e : list)
            if (p.test(e))
                result.add(e);
        return result;
    }
}
